/*
 * ebcd_epch.c - EBCD external PCH
 * Clunix, KyungWoon Cho, cezanne@clunix.com, 2006.2
 *
 * Copyright (c) 2001-2011 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "ebcd.h"
#include "monitor.h"
#include "audit.h"

static ecm_hashtable_t	epch_hash;
static ecm_mutex_t	epchs_mutex;

ecm_uint32	n_ext_pchs;

extern void do_switch_pktb(ebcd_pch_t *dpch_src, ebcd_pch_t *dpch_dst, ebc_pktb_t *pktb);

ebcd_pch_t *
ebcd_add_epch(ebc_pch_t *pchp)
{
	ebcd_pch_t	*epch;

	ecm_mutex_lock(epchs_mutex);
	if (ecm_hashtable_get(epch_hash, sizeof(ebc_ndid_t), &pchp->ndid)) {
		ecm_mutex_unlock(epchs_mutex);
		return NULL;
	}

	pchp->mdid = EBC_MDID_EBCD;

	epch = create_ebcd_pch(pchp, EXT_PCH);

	epch->u.ext.emoninfo = create_emoninfo();
	epch->u.ext.ecallinfo = create_ecallinfo();

	ecm_hashtable_insert(epch_hash, epch, sizeof(ebc_ndid_t), &pchp->ndid);
	n_ext_pchs++;

	change_static_route_status(pchp->ndid, ECM_TRUE);

	ecm_mutex_unlock(epchs_mutex);
	return epch;
}

void
ebcd_free_epch(ebcd_pch_t *epch)
{
	ebc_ndid_t	ndid = epch->pchp->ndid;

	ecm_mutex_lock(epchs_mutex);
	ecm_hashtable_remove(epch_hash, sizeof(ebc_ndid_t), &ndid);
	n_ext_pchs--;
	change_static_route_status(epch->pchp->ndid, ECM_FALSE);
	ecm_mutex_unlock(epchs_mutex);

	destroy_emoninfo(epch->u.ext.emoninfo);
	destroy_ecallinfo(epch->u.ext.ecallinfo);
}

ebcd_pch_t *
ebcd_get_epch(ebc_ndid_t ndid)
{
	ebcd_pch_t	*epch;

	ecm_mutex_lock(epchs_mutex);

	epch = ecm_hashtable_get(epch_hash, sizeof(ebc_ndid_t), &ndid);
	if (epch == NULL) {
		ecm_mutex_unlock(epchs_mutex);
		return NULL;
	}

	ecm_mutex_lock(epch->mutex);
	if (epch->destroyed) {
		ecm_mutex_unlock(epch->mutex);
		ecm_mutex_unlock(epchs_mutex);
		return NULL;
	}
	epch->refcnt++;
	ecm_mutex_unlock(epch->mutex);
	ecm_mutex_unlock(epchs_mutex);

	return epch;
}

ebcd_pch_t *
ebcd_find_epch(ebc_ndid_t ndid)
{
	ebcd_pch_t	*epch;

	ecm_mutex_lock(epchs_mutex);

	epch = ecm_hashtable_get(epch_hash, sizeof(ebc_ndid_t), &ndid);
	if (epch == NULL || epch->destroyed) {
		ecm_mutex_unlock(epchs_mutex);
		return NULL;
	}

	ecm_mutex_lock(epch->mutex);
	epch->refcnt++;
	ecm_mutex_unlock(epch->mutex);
	ecm_mutex_unlock(epchs_mutex);

	return epch;
}

ECMBOOL
ebcd_has_epch(ebc_ndid_t ndid)
{
	ECMBOOL	exist = ECM_FALSE;

	ecm_mutex_lock(epchs_mutex);
	if (ecm_hashtable_get(epch_hash, sizeof(ebc_ndid_t), &ndid))
		exist = ECM_TRUE;
	ecm_mutex_unlock(epchs_mutex);

	return exist;
}

static ecm_ptrs_t *
get_epchs(void)
{
	ecm_ptrs_t	*epchs;
	ebcd_pch_t	*epch;
	ecm_ht_iterator_t	iterator;

	ecm_mutex_lock(epchs_mutex);
	if (ecm_hashtable_getsize(epch_hash) == 0) {
		ecm_mutex_unlock(epchs_mutex);
		return NULL;
	}
	epchs = ecm_ptrs_new(16);
	for (epch = ecm_hashtable_first(epch_hash, &iterator, NULL);
	     epch;
	     epch = ecm_hashtable_next(epch_hash, &iterator, NULL)) {
		if (ebcd_pch_get(epch))
			ecm_ptrs_add(epchs, epch);
	}
	ecm_mutex_unlock(epchs_mutex);
	return epchs;
}

static void
put_epchs(ecm_ptrs_t *epchs)
{
	ecm_uint32	i;

	for (i = 0; i < epchs->count; i++)
		ebcd_pch_put((ebcd_pch_t *)epchs->pdata[i]);
	ecm_ptrs_free(epchs);
}

void
ebcd_broadcast_pktb(ebcd_pch_t *dpch_src, ebc_pktb_t *pktb)
{
	ecm_ptrs_t	*epchs;
	ebcd_pch_t	*epch;
	ebc_pktb_t	*pktb_dup;
	ecm_uint32	i;

	epchs = get_epchs();
	if (epchs == NULL)
		return;

	for (i = 0; i < epchs->count; i++) {
		epch = (ebcd_pch_t *)epchs->pdata[i];
		pktb_dup = ebc_pktb_dup(pktb);
		pktb_dup->pkthdr.mhdr.dstaddr.ndid = epch->pchp->ndid;
		ebc_pkthdr_encode(pktb_dup);
		do_switch_pktb(dpch_src, epch, pktb_dup);
	}
	put_epchs(epchs);
}

sr_t *
ebcd_get_all_ndids(void)
{
	sr_t	*sr_ndids = sr_create_dir();
	ebcd_pch_t	*dpch;
	ecm_ht_iterator_t	iterator;

	ecm_mutex_lock(epchs_mutex);
	for (dpch = ecm_hashtable_first(epch_hash, &iterator, NULL);
	     dpch;
	     dpch = ecm_hashtable_next(epch_hash, &iterator, NULL))
		sr_append(sr_ndids, sr_create_int(ST_INT, dpch->pchp->ndid));
	ecm_mutex_unlock(epchs_mutex);

	return sr_ndids;
}

void
ebcd_init_epchs(void)
{
	epch_hash = ecm_hashtable_create(32);
	epchs_mutex = ecm_mutex_create(ECM_FALSE);
}

#ifdef ECM_DEBUG

ecm_uint32
get_epchs_count(void)
{
	ecm_uint32	count;

	ecm_mutex_lock(epchs_mutex);
	count = ecm_hashtable_getsize(epch_hash);
	ecm_mutex_unlock(epchs_mutex);

	return count;
}

#endif

